// 阻塞队列的实现
#pragma once
#include <iostream>
#include <string.h>
#include <queue>
#include <pthread.h>
#include "Task.hpp"
using namespace std;

const int defaultcap = 5; // 队列默认容量先设为5

template <class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _q.size() >= _cap;
    }

    bool IsEmpty()
    {
        return _q.empty();
    }

public:
    BlockQueue(int cap = defaultcap)
        : _cap(cap),
          _csleep_num(0),
          _psleep_num(0)
    {
        // 锁和条件变量初始化
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_cond_init(&_empty_cond, nullptr);
    }

    // 入队列
    void Equeue(const T &in)
    {
        pthread_mutex_lock(&_mutex);
        // 生产者调用
        while (IsFull())
        {
            // 应该让生产者线程进行等待
            // 重点1：pthread_cond_wait调用成功，挂起当前线程前要先自动释放锁
            // 重点2：当前线程被唤醒时，默认就在临界区内唤醒，要从pthread_cond_wait
            //       成功返回，需要当前线程重新申请_mutex锁
            // 重点3：如果线程被唤醒但是申请锁失败了，就会在锁上阻塞等待
            _psleep_num++;
            // 问题1：pthread_cond_wait是可能失败的，失败会立即返回
            // 问题2：pthread_cond_wait可能会因为条件其实不满足
            //       导致伪唤醒，所以不能用if条件判断，得用while循环
            pthread_cond_wait(&_full_cond, &_mutex);
            _psleep_num--;
        }
        // 暂定：队列有空间
        _q.push(in);

        if (_csleep_num > 0) // 如果此时消费者在休眠等待
        {
            // 生产者来唤醒消费者
            pthread_cond_signal(&_empty_cond);
            cout << "唤醒消费者..." << endl;
        }

        // 直接这样来唤醒也没问题
        // pthread_cond_signal(&_empty_cond); // 在解锁之前唤醒可以
        pthread_mutex_unlock(&_mutex);
        // pthread_cond_signal(&_empty_cond); // 在解锁之后唤醒也可以，都是在锁上等待
    }

    // 出队列
    T Pop()
    {
        // 消费者调用
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        {
            // 应该让消费者线程进行等待
            _csleep_num++;
            pthread_cond_wait(&_empty_cond, &_mutex);
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();

        if (_psleep_num > 0) // 如果此时生产者在休眠等待
        {
            // 消费者来唤醒生产者
            pthread_cond_signal(&_full_cond);
            cout << "唤醒生产者..." << endl;
        }
        pthread_mutex_unlock(&_mutex);
        return data;
    }

    ~BlockQueue()
    {
        // 销毁锁和条件变量
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full_cond);
        pthread_cond_destroy(&_empty_cond);
    }

private:
    queue<T> _q;                // 临界资源
    int _cap;                   // 队列容量大小
    pthread_mutex_t _mutex;     // 需要锁来进行互斥
    pthread_cond_t _full_cond;  // 生产者把队列入满了需要用条件变量进行等待、唤醒
    pthread_cond_t _empty_cond; // 消费者把队列出空了需要用条件变量进行等待、唤醒
    int _csleep_num;            // 消费者休眠的个数
    int _psleep_num;            // 生产者休眠的个数
};